/*
  Losu Copyright Notice
  --------------------
    Losu is an open source programming language project under the MIT license
  that can be used for both academic and commercial purposes. There are no
  fees, no royalties, and no GNU-like "copyright" restrictions. Losu qualifies
  as open source software. However, Losu is not public property, and founder
  'chen-chaochen' retains its copyright.

    Losu has been registered with the National Copyright Administration of the
  People's Republic of China, and adopts the MIT license as the copyright
  licensing contract under which the right holder conditionally licenses its
  reproduction, distribution, and modification rights to an unspecified public.

    If you use Losu, please follow the public MIT agreement or choose to enter
  into a dedicated license agreement with us.

  The MIT LICENSE is as follows
  --------------------
  Copyright  2020 - now  chen-chaochen

    Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the “Software”), to
  deal in the Software without restriction, including without limitation the
  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

/**
 * An ultra-lightweight stacked virtual machine
 * consisting of vmHeap,vmCore
 */

#include "losu_vm.h"
#include "losu.h"
#include "losu_bytecode.h"
#include "losu_errmsg.h"
#include "losu_gc.h"
#include "losu_malloc.h"
#include "losu_object.h"
#include "losu_syntax.h"
#include <math.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef define_source_losu_vm
#define define_source_losu_vm
/* static segment */
static LosuObj *__losuVmheapCallCapi (LosuVm *vm, _inlineFunction *cl,
                                      LosuObj *base);
/* static segment */
static LosuObj *__losuVmcoreExec (LosuVm *vm, const _inlineFunction *func,
                                  LosuObj *base);
static const LosuObj *__losuVmcoreGetunit (LosuVm *vm, LosuObj *obj);
static void __losuVmcoreSetunit (LosuVm *vm, LosuObj *t, LosuObj *key);
static const LosuObj *__losuVmcoreGetglobal (LosuVm *vm, _inlineString *name);
static void __losuVmcoreSetglobal (LosuVm *vm, _inlineString *name);
static _l_bool __losuVmcoreLessthan (LosuVm *vm, const LosuObj *l,
                                     const LosuObj *r, LosuObj *top);
static void __losuVmcoreStrconcat (LosuVm *vm, LosuObj *top);
static _l_bool __losuVmcoreStrisLess (const _inlineString *ls,
                                      const _inlineString *rs);

static void __losuVmcoreAdjVarg (LosuVm *vm, LosuObj *base, int32_t nfixarg);
static _inlineFunction *__losuVmcoreFunction (LosuVm *vm, int32_t niss);
#endif /* define_source_losu_vm */

#ifndef define_source_losu_vm_heap
#define define_source_losu_vm_heap

#define __rstoreStackLimit(vm)                                                \
  {                                                                           \
    if (vm->top - vm->stack < vm->stacksize - 1)                              \
      vm->stackmax = vm->stack + (vm->stacksize - 1);                         \
  }

void
__losu_vmHeap_init (LosuVm *vm, int32_t size)
{
  vm->stack = __losu_mem_newvector (vm, size, LosuObj);
  vm->nblocks += size * sizeof (LosuObj);
  vm->stackmax = vm->stack + size - 1;
  vm->stacksize = size;
  vm->base = vm->top = vm->stack;
}

void
__losu_vmHeap_check (LosuVm *vm, int32_t size)
{
  if (vm->stackmax - vm->top <= size)
    vm_error (vm, LosuErrorCode_Heap,
              __config_losucore_errmsg_msgStackOverflow);
}
void
__losu_vmHeap_break (LosuVm *vm, int16_t ecode)
{
  if (vm->errjmp)
    {
      vm->errjmp->err = ecode;
      longjmp (vm->errjmp->jmpflag, 1);
    }
  else
    {
      if (vm->emjmp)
        longjmp (vm->emjmp->jmpflag, 1);
      exit (LosuErrorCode_Over);
    }
}
void
__losu_vmHeap_adjTop (LosuVm *vm, LosuObj *base, int32_t size)
{
  int32_t diff = size - (vm->top - base);
  if (diff <= 0)
    vm->top = base + size;
  else
    {
      __losu_vmHeap_check (vm, diff);
      while (diff--)
        ovtype ((vm->top++)) = LosuTypeDefine_null;
    }
}
int32_t
__losu_vmHeap_callS (LosuVm *vm, int32_t arg, int32_t nres)
{
  LosuObj *func = vm->top - arg - 1;
  int32_t sta = 0;
  LosuObj *oldBase = vm->base;
  LosuObj *oldTop = vm->top;
  __longjump jmp = {
    .err = 0,
    .pre = vm->errjmp,
  };
  vm->errjmp = &jmp;
  if (setjmp (jmp.jmpflag) == 0)
    __losu_vmHeap_call (vm, func, nres);
  else
    {
      vm->base = oldBase;
      vm->top = oldTop;
      __rstoreStackLimit (vm);
    }
  vm->errjmp = jmp.pre;
  sta = jmp.err;
  vm->top = func;
  return sta;
}
void
__losu_vmHeap_call (LosuVm *vm, LosuObj *func, int32_t nres)
{
  LosuObj *fres;
  _inlineCallinfo cinfo;
  _inlineFunction *fc;
  if (ovtype ((func)) != LosuTypeDefine_function)
    {
      if (ovtype (func) == LosuTypeDefine_unit)
        {
          /* get overload func */
          LosuObj *tfunc = obj_indexunitbystr (vm, *func, "::()");
          if (ovtype (tfunc) != LosuTypeDefine_function)
            vm_error (vm, LosuErrorCode_Runtime,
                      __config_losucore_errmsg_msgInvalidOverload, "()");

          /* copy stack args */
          vm->top++;
          for (LosuObj *i = vm->top - 1; i > func; i--)
            *i = *(i - 1);

          /* set tfunc */
          *func = *tfunc;
        }
      else
        vm_error (vm, LosuErrorCode_Runtime,
                  __config_losucore_errmsg_msgCalledNotFunc);
    }

  fc = ovfunc ((func));
  cinfo.func = fc;
  ovcall ((func)) = &cinfo;
  ovtype ((func))
      = LosuTypeDefine_mark; /* mark function from GC, when calling */

  fres = (fc->isC) ? (__losuVmheapCallCapi (vm, fc, func + 1))
                   : (__losuVmcoreExec (vm, fc, func + 1));
  if (nres == -1)
    {
      while (fres < vm->top)
        *func++ = *fres++;
      vm->top = func;
    }
  else
    {
      for (; nres > 0 && fres < vm->top; nres--)
        *func++ = *fres++;
      vm->top = func;
      __losu_vmHeap_check (vm, nres);
      for (; nres > 0; nres--)
        {
          ovtype ((vm->top)) = LosuTypeDefine_null;
          vm->top++;
        }
    }
  /* __losu_gc_checkClt (vm); */
}

static LosuObj *
__losuVmheapCallCapi (LosuVm *vm, _inlineFunction *cl, LosuObj *base)
{
  int32_t nc = cl->nclosure;
  LosuObj *oldBase = vm->base;
  int32_t n = 0;
  vm->base = base;
  __losu_vmHeap_check (vm, nc);
  for (n = 0; n < nc; n++)
    *(vm->top++) = cl->closure[n];
  n = ((*cl->func.capi) (vm));
  vm->base = oldBase;
  return vm->top - n;
}

#undef __rstoreStackLimit
#endif /* define_source_losu_vm_heap */

#ifndef define_source_losu_vm_core
#define define_source_losu_vm_core

static LosuObj *
__losuVmcoreExec (LosuVm *vm, const _inlineFunction *func, LosuObj *base)
{
#define tonumber(o)                                                           \
  ((ovtype (o) != LosuTypeDefine_number)                                      \
   && (__losu_vmCore_Tonum (vm, o, 1)) != 0)
#define tostring(o)                                                           \
  ((ovtype (o) != LosuTypeDefine_string)                                      \
   && (__losu_vmCore_Tostr (vm, o, 1) != 0))
#define dojmp(pc, i)                                                          \
  {                                                                           \
    pc += cgIns_GetS (i);                                                     \
  }

  /* printf("P1\n"); */

  /* Coro Env */
  LosuObj *top;
  const _inlineScode *fcode = (const _inlineScode *)func->func.sdef;
  const vmInstruction *pc = (const vmInstruction *)fcode->code;
  const _inlineString **lcstr = (const _inlineString **)fcode->lcstr;
  const _l_number *lcnum = (const _l_number *)fcode->lcnum;
  const LosuObj *closure = (const LosuObj *)func->closure;
  const _inlineScode **lcfcode = (const _inlineScode **)fcode->lcscode;

  ovcall ((base - 1))->pc = &pc;

  /* Check Stack & Adj Varlist */
  __losu_vmHeap_check (vm, fcode->maxstacksize + 8);
  if (fcode->isVarg)
    __losuVmcoreAdjVarg (vm, base, fcode->narg);
  else
    __losu_vmHeap_adjTop (vm, base, fcode->narg);
  top = vm->top;

  while (1)
    {
      const vmInstruction i = *pc++;
      /* printf("Op:\t%d\n",cgIns_GetOp (i)); */
      switch (cgIns_GetOp (i))
        {
        case INS_END:
          {
            vm->top = top;
            return top;
          }
        case INS_RETURN:
          {
            vm->top = top;
            return base + cgIns_GetU (i);
          }
        case INS_CALL:
          {
            int16_t nres = cgIns_GetB (i);
            if (nres == 255)
              nres = -1;
            vm->top = top;
            __losu_vmHeap_call (vm, base + cgIns_GetA (i), nres);
            top = vm->top;
            __losu_gc_checkClt (vm);
            break;
          }
        case INS_PUSHNULL:
          {
            vmIns_U n = cgIns_GetU (i);
            do
              {
                ovtype ((top++)) = LosuTypeDefine_null;
              }
            while (--n > 0);
            break;
          }
        case INS_POP:
          {
            top -= cgIns_GetU (i);
            break;
          }
        case INS_PUSHSTRING:
          {
            ovtype (top) = LosuTypeDefine_string;
            ovIstr (top) = (_inlineString *)lcstr[cgIns_GetU (i)];
            top++;
            break;
          }
        case INS_PUSHNUM:
          {
            ovtype (top) = LosuTypeDefine_number;
            ovnumber (top) = lcnum[cgIns_GetU (i)];
            top++;
            break;
          }
        case INS_PUSHUPVALUE:
          {
            *top++ = closure[cgIns_GetU (i)];
            break;
          }
        case INS_GETLOCAL:
          {
            *top++ = *(base + cgIns_GetU (i));
            break;
          }
        case INS_GETGLOBAL:
          {
            vm->top = top;
            *top = *__losuVmcoreGetglobal (
                vm, (_inlineString *)lcstr[cgIns_GetU (i)]);
            top++;
            break;
          }
        case INS_GETUNIT:
          {
            vm->top = top;
            top--;
            *(top - 1) = *__losuVmcoreGetunit (vm, top - 1);
            break;
          }
        case INS_PUSHSELF:
          {
            LosuObj tmp;
            tmp = *(top - 1);
            ovtype (top) = LosuTypeDefine_string;
            ovIstr ((top++)) = (_inlineString *)lcstr[cgIns_GetU (i)];
            vm->top = top;
            *(top - 2) = *__losuVmcoreGetunit (vm, top - 2);
            *(top - 1) = tmp;
            break;
          }
        case INS_CREATEUNIT:
          {
            vm->top = top;
            __losu_gc_checkClt (vm);
            ovhash (top) = __losu_objUnit_new (vm, cgIns_GetU (i));
            ovtype (top) = LosuTypeDefine_unit;
            top++;
            break;
          }
        case INS_SETLOCAL:
          {
            *(base + cgIns_GetU (i)) = *(--top);
            break;
          }
        case INS_SETGLOBAL:
          {
            vm->top = top;
            __losuVmcoreSetglobal (vm, (_inlineString *)lcstr[cgIns_GetU (i)]);
            top--;
            break;
          }
        case INS_SETUNIT:
          {
            LosuObj *t = top - cgIns_GetA (i);
            vm->top = top;
            __losuVmcoreSetunit (vm, t, t + 1);
            top -= cgIns_GetB (i);
            break;
          }
        case INS_SETLIST:
          {
            uint32_t aux = cgIns_GetA (i) * vmOl_MaxSetlist;
            vmIns_B n = cgIns_GetB (i);
            _inlineHash *arr = ovhash ((top - n - 1));
            vm->top = top - n;
            for (; n; n--)
              *__losu_objUnit_setnum (vm, arr, aux + n) = *(--top);
            break;
          }
        case INS_SETMAP:
          {
            vmIns_U n = cgIns_GetU (i);
            LosuObj *ftop = top - 2 * n;
            _inlineHash *arr = ovhash ((ftop - 1));
            vm->top = ftop;
            for (; n; n--)
              {
                top -= 2;
                *__losu_objUnit_set (vm, arr, top) = *(top + 1);
              }
            break;
          }
        case INS_ADD:
          {

            if (tonumber ((top - 2)) || tonumber ((top - 1)))
              {
                /* __losu_vmHeap_check (vm, 6); */
                top += 2;
                vm->top = top;
                *(top - 1) = (LosuObj){
                  .type = LosuTypeDefine_string,
                  .value.str = __losu_objString_new (vm, "::+"),
                };
                *(top - 2) = *(top - 4);
                *(top) = *__losuVmcoreGetunit (vm, top - 2);
                top += 3;
                vm->top = top;
                *(top - 2) = *(top - 7);
                *(top - 1) = *(top - 6);

                if (ovtype ((top - 3)) != LosuTypeDefine_function)
                  vm_error (vm, LosuErrorCode_Runtime,
                            __config_losucore_errmsg_msgInvalidOverload, "+");

                __losu_vmHeap_call (vm, top - 3, 1);
                *(top - 7) = *(vm->top - 1);
                top -= 6;
                vm->top = top;
                break;
              }
            else
              ovnumber ((top - 2)) += ovnumber ((top - 1));
            top--;
            break;
          }
        case INS_SUB:
          {
            if (tonumber ((top - 2)) || tonumber ((top - 1)))
              {
                top += 2;
                vm->top = top;
                *(top - 1) = (LosuObj){
                  .type = LosuTypeDefine_string,
                  .value.str = __losu_objString_new (vm, "::-"),
                };
                *(top - 2) = *(top - 4);
                *(top) = *__losuVmcoreGetunit (vm, top - 2);
                top += 3;
                vm->top = top;
                *(top - 2) = *(top - 7);
                *(top - 1) = *(top - 6);
                if (ovtype ((top - 3)) != LosuTypeDefine_function)
                  vm_error (vm, LosuErrorCode_Runtime,
                            __config_losucore_errmsg_msgInvalidOverload, "-");
                __losu_vmHeap_call (vm, top - 3, 1);
                *(top - 7) = *(vm->top - 1);
                top -= 6;
                vm->top = top;
                break;
              }
            else
              ovnumber ((top - 2)) -= ovnumber ((top - 1));
            top--;
            break;
          }
        case INS_MULT:
          {
            if (tonumber ((top - 2)) || tonumber ((top - 1)))
              {
                top += 2;
                vm->top = top;
                *(top - 1) = (LosuObj){
                  .type = LosuTypeDefine_string,
                  .value.str = __losu_objString_new (vm, "::*"),
                };
                *(top - 2) = *(top - 4);
                *(top) = *__losuVmcoreGetunit (vm, top - 2);
                top += 3;
                vm->top = top;
                *(top - 2) = *(top - 7);
                *(top - 1) = *(top - 6);
                if (ovtype ((top - 3)) != LosuTypeDefine_function)
                  vm_error (vm, LosuErrorCode_Runtime,
                            __config_losucore_errmsg_msgInvalidOverload, "*");
                __losu_vmHeap_call (vm, top - 3, 1);
                *(top - 7) = *(vm->top - 1);
                top -= 6;
                vm->top = top;
                break;
              }
            else
              ovnumber ((top - 2)) *= ovnumber ((top - 1));
            top--;
            break;
          }
        case INS_DIV:
          {
            if (tonumber ((top - 2)) || tonumber ((top - 1)))
              {
                top += 2;
                vm->top = top;
                *(top - 1) = (LosuObj){
                  .type = LosuTypeDefine_string,
                  .value.str = __losu_objString_new (vm, "::/"),
                };
                *(top - 2) = *(top - 4);
                *(top) = *__losuVmcoreGetunit (vm, top - 2);
                top += 3;
                vm->top = top;
                *(top - 2) = *(top - 7);
                *(top - 1) = *(top - 6);
                if (ovtype ((top - 3)) != LosuTypeDefine_function)
                  vm_error (vm, LosuErrorCode_Runtime,
                            __config_losucore_errmsg_msgInvalidOverload, "/");
                __losu_vmHeap_call (vm, top - 3, 1);
                *(top - 7) = *(vm->top - 1);
                top -= 6;
                vm->top = top;
                break;
              }
            else
              ovnumber ((top - 2)) /= ovnumber ((top - 1));
            top--;
            break;
          }
        case INS_POW:
          {
            if (tonumber ((top - 2)) || tonumber ((top - 1)))
              {
                top += 2;
                vm->top = top;
                *(top - 1) = (LosuObj){
                  .type = LosuTypeDefine_string,
                  .value.str = __losu_objString_new (vm, "::^"),
                };
                *(top - 2) = *(top - 4);
                *(top) = *__losuVmcoreGetunit (vm, top - 2);
                top += 3;
                vm->top = top;
                *(top - 2) = *(top - 7);
                *(top - 1) = *(top - 6);
                if (ovtype ((top - 3)) != LosuTypeDefine_function)
                  vm_error (vm, LosuErrorCode_Runtime,
                            __config_losucore_errmsg_msgInvalidOverload, "^");
                __losu_vmHeap_call (vm, top - 3, 1);
                *(top - 7) = *(vm->top - 1);
                top -= 6;
                vm->top = top;
                break;
              }
            else
              ovnumber ((top - 2))
                  = pow (ovnumber ((top - 2)), ovnumber ((top - 1)));
            top--;
            break;
          }
        case INS_CONCAT:
          {
            __losuVmcoreStrconcat (vm, top);
            top--;
            vm->top = top;
            __losu_gc_checkClt (vm);
            break;
          }
        case INS_NEG:
          {
            if (tonumber ((top - 1)))
              {
                vm->top = top;
                LosuObj *func = obj_indexunitbystr (vm, *(top - 1), "::-x");
                if (ovtype (func) == LosuTypeDefine_null)
                  vm_error (vm, LosuErrorCode_Runtime,
                            __config_losucore_errmsg_msgInvalidOverload,
                            "::-x");
                stack_push (vm, *func);      /* push function */
                stack_push (vm, *(top - 1)); /* push operand */
                stack_call (vm, 1, 1);
                *(top - 1) = *(vm->top - 1);
                vm->top = top;
              }
            else
              ovnumber ((top - 1)) = -ovnumber ((top - 1));
            break;
          }
        case INS_NOT:
          {
            ovtype ((top - 1)) = (ovtype ((top - 1)) == LosuTypeDefine_null)
                                     ? LosuTypeDefine_number
                                     : LosuTypeDefine_null;
            ovnumber ((top - 1)) = 1;
            break;
          }
        case INS_JMPNE:
          {
            top -= 2;
            if (!__losu_object_isObjEqual (top, top + 1))
              dojmp (pc, i);
            break;
          }
        case INS_JMPEQ:
          {
            top -= 2;
            if (__losu_object_isObjEqual (top, top + 1))
              dojmp (pc, i);
            break;
          }
        case INS_JMPLT:
          {
            top -= 2;
            if (__losuVmcoreLessthan (vm, top, top + 1, top + 2))
              dojmp (pc, i);
            break;
          }
        case INS_JMPLE:
          {
            top -= 2;
            if (!__losuVmcoreLessthan (vm, top + 1, top, top + 2))
              dojmp (pc, i);
            break;
          }
        case INS_JMPGT:
          {
            top -= 2;
            if (__losuVmcoreLessthan (vm, top + 1, top, top + 2))
              dojmp (pc, i);
            break;
          }
        case INS_JMPGE:
          {
            top -= 2;
            if (!__losuVmcoreLessthan (vm, top, top + 1, top + 2))
              dojmp (pc, i);
            break;
          }
        case INS_JMPT:
          {
            if (ovtype ((--top)) != LosuTypeDefine_null)
              dojmp (pc, i);
            break;
          }
        case INS_JMPF:
          {
            if (ovtype ((--top)) == LosuTypeDefine_null)
              dojmp (pc, i);
            break;
          }
        case INS_JMPONT:
          {
            if (ovtype ((top - 1)) == LosuTypeDefine_null)
              top--;
            else
              dojmp (pc, i);
            break;
          }
        case INS_JMPONF:
          {
            if (ovtype ((top - 1)) != LosuTypeDefine_null)
              top--;
            else
              dojmp (pc, i);
            break;
          }
        case INS_JMP:
          {
            dojmp (pc, i);
            break;
          }
        case INS_PUSHNULLJMP:
          {
            ovtype ((top++)) = LosuTypeDefine_null;
            pc++;
            break;
          }
        case INS_FORPREP:
          {
            if (tonumber ((top - 1)))
              vm_error (vm, LosuErrorCode_Runtime,
                        __config_losucore_errmsg_msgInvalidForstep);
            if (tonumber ((top - 2)))
              vm_error (vm, LosuErrorCode_Runtime,
                        __config_losucore_errmsg_msgInvalidFormax);
            if (tonumber ((top - 3)))
              vm_error (vm, LosuErrorCode_Runtime,
                        __config_losucore_errmsg_msgInvalidForinit);
            if (ovnumber ((top - 1)) > 0
                    ? ovnumber ((top - 3)) > ovnumber ((top - 2))
                    : ovnumber ((top - 3)) < ovnumber ((top - 2)))
              {
                top -= 3;
                dojmp (pc, i);
              }
            break;
          }
        case INS_FORLOOP:
          {
            if (ovtype ((top - 3)) != LosuTypeDefine_number)
              vm_error (vm, LosuErrorCode_Runtime,
                        __config_losucore_errmsg_msgInvalidForstep);
            ovnumber ((top - 3)) += ovnumber ((top - 1));
            if (ovnumber ((top - 1)) > 0
                    ? ovnumber ((top - 3)) > ovnumber ((top - 2))
                    : ovnumber ((top - 3)) < ovnumber ((top - 2)))
              top -= 3;
            else
              dojmp (pc, i);
            break;
          }
        case INS_LFORPREP:
          {
            LosuNode *node;
            if (ovtype ((top - 1)) != LosuTypeDefine_unit)
              vm_error (vm, LosuErrorCode_Runtime,
                        __config_losucore_errmsg_msgInvalidForobj);
            node = (LosuNode *)__losu_objUnit_getnext (
                ovhash ((top - 1)), (LosuObj *)&_inlineNullObj);
            if (node == NULL)
              {
                top--;
                dojmp (pc, i);
              }
            else
              {
                top += 2;
                *(top - 2) = node->key;
                *(top - 1) = node->value;
              }
            break;
          }
        case INS_LFORLOOP:
          {
            LosuNode *node;
            node = (LosuNode *)__losu_objUnit_getnext (ovhash ((top - 3)),
                                                       top - 2);
            if (node == NULL)
              top -= 3;
            else
              {
                *(top - 2) = node->key;
                *(top - 1) = node->value;
                dojmp (pc, i);
              }
            break;
          }
        case INS_PUSHFUNCTION:
          {
            vm->top = top;
            {
              _inlineFunction *func
                  = __losuVmcoreFunction (vm, cgIns_GetB (i));
              func->func.sdef = (_inlineScode *)lcfcode[cgIns_GetA (i)];
              func->isC = 0;
            }
            top = vm->top;
            __losu_gc_checkClt (vm);
            break;
          }
        default:
          {
            vm_error (vm, LosuErrorCode_Runtime,
                      __config_losucore_errmsg_msgInvalidBytecode);
          }
        }
    }

#undef tonumber
#undef tostring
#undef dojmp
}

_l_bool
__losu_vmCore_Tonum (LosuVm *vm, LosuObj *obj, _l_bool isCore)
{
  if (ovtype (obj) != LosuTypeDefine_string)
    {
      if (ovtype (obj) == LosuTypeDefine_unit)
        {
          LosuObj *func = obj_indexunitbystr (vm, *obj, "::->num");
          if (ovtype (func) == LosuTypeDefine_function)
            {
              LosuObj *oldtop = vm->top;
              if (isCore)
                vm->top = obj + 4;
              __losu_vmHeap_check (vm, 16);
              stack_push (vm, *func);
              stack_push (vm, *obj);
              stack_call (vm, 1, 1);
              *obj = *(vm->top - 1);
              vm->top = oldtop;
              return 0;
            }
        }
      return 1;
    }
  else
    {
      if (!__losu_object_str2num (ovSstr (obj), &ovnumber (obj)))
        return 1;
      ovtype (obj) = LosuTypeDefine_number;
      return 0;
    }
}

_l_bool
__losu_vmCore_Tostr (LosuVm *vm, LosuObj *obj, _l_bool isCore)
{
  if (ovtype (obj) != LosuTypeDefine_number)
    {
      if (ovtype (obj) == LosuTypeDefine_unit)
        {
          LosuObj *func = obj_indexunitbystr (vm, *obj, "::->str");
          if (ovtype (func) == LosuTypeDefine_function)
            {
              LosuObj *oldtop = vm->top;
              if (isCore)
                vm->top = obj + 4;
              __losu_vmHeap_check (vm, 16);
              stack_push (vm, *func);
              stack_push (vm, *obj);
              stack_call (vm, 1, 1);
              *obj = *(vm->top - 1);
              vm->top = oldtop;
              return 0;
            }
        }
      return 1;
    }
  else
    {
      char s[32] = { 0 };
      sprintf (s, "%.16g", ovnumber (obj));
      ovtype (obj) = LosuTypeDefine_string;
      ovIstr (obj) = __losu_objString_new (vm, s);
      return 0;
    }
}

static const LosuObj *
__losuVmcoreGetunit (LosuVm *vm, LosuObj *obj)
{
  if (ovtype (obj) == LosuTypeDefine_null)
    return &_inlineNullObj;
  if (ovtype (obj) == LosuTypeDefine_unit)
    {
      const LosuObj *h = __losu_objUnit_get (ovhash (obj), vm->top - 1);
      if (ovtype (h) == LosuTypeDefine_null)
        return &_inlineNullObj;
      return h;
    }
  return &_inlineNullObj;
}

static void
__losuVmcoreSetunit (LosuVm *vm, LosuObj *t, LosuObj *key)
{
  if (ovtype (t) == LosuTypeDefine_unit)
    *__losu_objUnit_set (vm, ovhash (t), key) = *(vm->top - 1);
  else
    vm_error (vm, LosuErrorCode_Runtime,
              __config_losucore_errmsg_msgInvalidSetobj);
}

static const LosuObj *
__losuVmcoreGetglobal (LosuVm *vm, _inlineString *name)
{
  return __losu_objUnit_getstr (vm->global, name);
}

static void
__losuVmcoreSetglobal (LosuVm *vm, _inlineString *name)
{
  const LosuObj *oval = __losu_objUnit_getstr (vm->global, name);
  if (oval != &_inlineNullObj)
    *((LosuObj *)oval) = *(vm->top - 1);
  else
    {
      LosuObj key = (LosuObj){
        .type = LosuTypeDefine_string,
        .value.str = name,
      };
      *__losu_objUnit_set (vm, vm->global, &key) = *(vm->top - 1);
    }
}

static _l_bool
__losuVmcoreLessthan (LosuVm *vm, const LosuObj *l, const LosuObj *r,
                      LosuObj *top)
{
  if (ovtype (l) != ovtype (r))
    {
      if (ovtype (l) == LosuTypeDefine_number
          && ovtype (r) == LosuTypeDefine_string)
        return ovnumber (l) < ostr2num (ovSstr (r), NULL);
      else if (ovtype (r) == LosuTypeDefine_number
               && ovtype (l) == LosuTypeDefine_string)
        return ostr2num (ovSstr (l), NULL) < ovnumber (r);
      else
        return 0;
    }
  switch (ovtype (l))
    {
    case LosuTypeDefine_number:
      return ovnumber (l) < ovnumber (r);
    case LosuTypeDefine_string:
      return __losuVmcoreStrisLess (ovIstr (l), ovIstr (r));
    default:
      break;
    }
  return ovtype (r) != LosuTypeDefine_null
         || ovtype (l) != LosuTypeDefine_null;
}

static void
__losuVmcoreStrconcat (LosuVm *vm, LosuObj *top)
{
#define tostring(o)                                                           \
  ((ovtype (o) != LosuTypeDefine_string)                                      \
   && (__losu_vmCore_Tostr (vm, o, 1) != 0))

  if (tostring ((top - 2)) || tostring ((top - 1)))
    {
      top += 2;
      vm->top = top;
      *(top - 1) = (LosuObj){
        .type = LosuTypeDefine_string,
        .value.str = __losu_objString_new (vm, "::&"),
      };
      *(top - 2) = *(top - 4);
      *(top) = *__losuVmcoreGetunit (vm, top - 2);

      top += 3;
      vm->top = top;
      *(top - 2) = *(top - 7);
      *(top - 1) = *(top - 6);
      if (ovtype ((top - 3)) != LosuTypeDefine_function)
        vm_error (vm, LosuErrorCode_Runtime,
                  __config_losucore_errmsg_msgInvalidOverload, "&");
      __losu_vmHeap_call (vm, top - 3, 1);
      *(top - 7) = *(vm->top - 1);
      top -= 6;
      vm->top = top;
      return;
    }
  else if (ovIstr ((top - 1))->len > 0)
    {
      _l_size_t p;
      _l_size_t nl = ovIstr ((top - 2))->len + ovIstr ((top - 1))->len;
      if (nl > vmOl_MaxStrlen)
        vm_error (vm, LosuErrorCode_Runtime,
                  __config_losucore_errmsg_msgStrlenOverflow);
      if (nl > vm->nbufftmp)
        {
          __losu_mem_reallocvector (vm, vm->bufftmp, nl, unsigned char);
          vm->nblocks += (nl - vm->nbufftmp) * sizeof (char);
          vm->nbufftmp = nl;
        }

      p = ovIstr ((top - 2))->len;
      memcpy (vm->bufftmp, ovSstr ((top - 2)), p);
      memcpy (vm->bufftmp + p, ovSstr ((top - 1)), ovIstr ((top - 1))->len);
      ovIstr ((top - 2))
          = __losu_objString_newstr (vm, (const char *)(vm->bufftmp), nl);
    }
    /* top--; */
#undef tostring
}

static _l_bool
__losuVmcoreStrisLess (const _inlineString *ls, const _inlineString *rs)
{

  const void *l = (const void *)(ls->str);
  const void *r = (const void *)(rs->str);
  _l_size_t ll = ls->len;
  _l_size_t lr = rs->len;
  _l_size_t ml = ll < lr ? ll : lr;
  int32_t i = memcmp (l, r, ml);

  return i < 0 ? 1 : ((i == 0) ? (ll < lr) : 0);
}

void
__losuVmcoreAdjVarg (LosuVm *vm, LosuObj *base, int32_t nfixarg)
{
  int32_t nv = (vm->top - base) - nfixarg;
  if (nv < 0)
    __losu_vmHeap_adjTop (vm, base, nfixarg);

  {
    LosuObj *felem = base + nfixarg;
    int32_t i;
    _inlineHash *hunit = __losu_objUnit_new (vm, 0);
    for (i = 0; felem + i < vm->top; i++)
      *__losu_objUnit_setnum (vm, hunit, i + 1) = *(felem + i);

    *__losu_objUnit_setstr (vm, hunit, __losu_objString_new (vm, "n"))
        = (LosuObj){
            .type = LosuTypeDefine_number,
            .value.num = i,
          };
#ifdef __config_losucore_feature_chKeyword
    *__losu_objUnit_setstr (vm, hunit, __losu_objString_new (vm, "个数"))
        = (LosuObj){
            .type = LosuTypeDefine_number,
            .value.num = i,
          };
#endif
    vm->top = felem;
    ovtype ((vm->top)) = LosuTypeDefine_unit;
    ovhash ((vm->top)) = hunit;
    if (vm->top == vm->stackmax)
      __losu_vmHeap_check (vm, 1);
    vm->top++;
  }
}

static _inlineFunction *
__losuVmcoreFunction (LosuVm *vm, int32_t niss)
{
  _inlineFunction *f = __losu_objFunc_new (vm, niss);
  vm->top -= niss;
  while (niss--)
    f->closure[niss] = *(vm->top + niss);
  ovfunc ((vm->top)) = f;
  ovtype ((vm->top)) = LosuTypeDefine_function;
  if (vm->top == vm->stackmax)
    __losu_vmHeap_check (vm, 1);
  vm->top++;
  return f;
}

#endif /* define_source_losu_vm_core */
